﻿using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.IO;

namespace Programmer_1
{
    class DataLogger
    {
        public delegate void StateChangedEventHandler(State oldState, State newState);
        public event StateChangedEventHandler StateChanged;

        public enum State { LOGGING_ON = 0x01, LOGGING_OFF = 0x02 }
        State currentState = State.LOGGING_OFF;
        public State CurrentState { get { return currentState; } }

        AppModel app;
        BlockingCollection<int[]> dataQueue;

        volatile bool stopLogData;
        volatile bool connected;

        public DataLogger(AppModel app)
        {
            this.app = app;
            app.Connected += OnProgrammerConnected;
            app.Disconnected += OnProgrammerDisconnected;
        }

        public void LogDataAsync(string filename)
        {
            dataQueue = new BlockingCollection<int[]>();//creating new as previous was made completed
            // subscribe
            app.DataRead += EnqueDataRead;
            Task.Run(() =>
            {
                _LogData(filename);
            });
        }

        void EnqueDataRead(int temp, int light, int rate)
        {
            dataQueue.TryAdd(new int[] { temp, light });// non-blocking, possible loss of data?
        }

        void _LogData(string filename)
        {
            stopLogData = false;
            SetState(State.LOGGING_ON);

            DateTime startTime = DateTime.Now;
            int samplesCollected = 0;

            // NOTE: using bad workaround to append header to file
            // 1. write to file w/o header
            // 2. create temporary file and copy header + original file content into it
            // 3. delete original, rename temporary
            using (System.IO.StreamWriter file = new System.IO.StreamWriter(filename))
            {
                for (int i = 1; !stopLogData; i++, samplesCollected++)
                {
                    try
                    {
                        int[] data = dataQueue.Take(); // blocking
                        int temp = data[0];
                        int light = data[1];
                        file.WriteLine("" + i + ",      "
                            + DateTime.Now.ToString("yyyy-MM-dd H-mm-ss-fff") + ",                    "
                            + temp + ",                       " + light);
                    }
                    catch (InvalidOperationException ex)
                    {
                        Debug.WriteLine("Exception: {0}", ex);
                    }
                }
            }

            // 2. appending header
            string tempfilename = Path.GetRandomFileName();
            tempfilename = Path.Combine(Path.GetTempPath(), tempfilename);
            Debug.WriteLine(tempfilename);

            using (StreamReader file = new StreamReader(filename))
            {
                using (StreamWriter tempfile = new StreamWriter(tempfilename, true))
                {
                    tempfile.WriteLine("Data logging started: [" + startTime.ToString("yyyy-MM-dd H-mm-ss") + "]");
                    tempfile.WriteLine("Data logging stopped: [" + DateTime.Now.ToString("yyyy-MM-dd H-mm-ss") + "]");
                    tempfile.WriteLine("Samples collectedc: [" + samplesCollected + "]");
                    tempfile.WriteLine("-----------------------------------------------------------------------------");
                    tempfile.WriteLine("#,      Date/Time,                      Temperature,                        Light");

                    string line = null;
                    while ((line = file.ReadLine()) != null)
                    {
                        tempfile.WriteLine(line);
                    }
                }
            }

            File.Delete(filename);
            File.Move(tempfilename, filename);

            SetState(State.LOGGING_OFF);
        }

        public void StopLogData()
        {
            // unsubscribe
            app.DataRead -= EnqueDataRead;
            stopLogData = true;
            dataQueue.CompleteAdding();
        }

        void SetState(State newState)
        {
            if (!connected)
            {
                newState = State.LOGGING_OFF;
            }
            State oldState = this.currentState;
            this.currentState = newState;
            if (oldState != newState)
            {
                StateChanged(oldState, newState);
            }
        }

        void OnProgrammerConnected(string progID)
        {
            connected = true;
        }

        void OnProgrammerDisconnected(string progID)
        {
            connected = false;
            SetState(State.LOGGING_OFF);
        }
    }
}
